pragma once
#include "cuda_runtime.h"
#include "include/esgSet.h"
#include "util/mgpucontext.h"
#include "helper_timer.h"
#include <tuple>
#include <vector>

#include "include/cudaEsg.h"


namespace cudaEsg
{

	class CudaComputeContext : public ComputeContext
	{
	public:
		CudaComputeContext();
		~CudaComputeContext();


		operation_errors_t ResetStatistics();

		operation_errors_t Ratings(int setId, int universeIndex, int weightMatrixId, unsigned char filter, RatingsResult &result);
		void ComputeRanking(RatingsResult &result, float* ratings_h, ESGSet* set);

		operation_errors_t AddUniverse(int setId, int universeId, int companiesCount, unsigned char * universe);
		operation_errors_t SetAttributes(int setId, int companiesCount, unsigned char * attributes);
		operation_errors_t AddMatrix(int setId, int matrixId, int companiesCount, float * weightsMatrix);

		operation_errors_t CudaComputeContext::AddSet(SetConfiguration configuration);
		operation_errors_t SetIndicators(int setId, int companiesCount, int indicatorsCount, float *indicatorScores);
		operation_errors_t DeleteSet(int setId);

		operation_errors_t FillSetAttributes(int setId, int &companiesCount, int &indicatorsCount, int &clusterGroupsCount, int &clustersCount);

	private:
		StopWatchInterface *_timer;
		mgpu::ContextPtr _mgpuContext;

		std::vector<std::tuple<int, ESGSet*>> *sets;
		cudaEsg::cudaEsg_errors_t CleanSet(ESGSet *set);

		void AddMemoryOperation(std::string name, double elapseSeconds);

		double CudaComputeContext::getOccupiedMemoryOnDevice()
		{
			// show memory usage of GPU

			size_t free_byte;

			size_t total_byte;

			cudaError_t cuda_status = cudaMemGetInfo(&free_byte, &total_byte);

			if (cudaSuccess != cuda_status){
				std::printf("Error: cudaMemGetInfo fails, %s \n", cudaGetErrorString(cuda_status));

				exit(1);
			}

			double used_db = (double)(free_byte);

			// printf("GPU memory usage: used = %f, free = %f MB, total = %f MB\n", used_db/1024.0/1024.0, free_db/1024.0/1024.0, total_db/1024.0/1024.0);
			return used_db;
		}

		ESGSet* CudaComputeContext::GetSet(int setId)  {
			auto iterator = sets->begin();
			for (; iterator != sets->end(); iterator++)
			{
				if (get<0>(*iterator) == setId)
					return get<1>(*iterator);
			}
			return nullptr;
		}
	};

}

